package xyz.zhzby.cyzx.oj.messenger;

import io.swagger.annotations.ApiModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import xyz.zhzby.cyzx.oj.entity.Submission;
import xyz.zhzby.cyzx.oj.service.ICheckpointService;
import xyz.zhzby.cyzx.oj.service.ISubmissionService;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import java.util.Calendar;
import java.util.Date;

/**
 * @author zby
 * @date 2022/02/05
 */
@Component
@Slf4j
@ApiModel("消息接收服务. 用于接收来自评测机的实时评测结果")
public class MessageReceiver implements MessageListener {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Autowired
    private ISubmissionService submissionService;

    @Autowired
    private ICheckpointService checkpointService;

    @Override
    public void onMessage(Message message) {
        if (message instanceof MapMessage) {
            final MapMessage mapMessage = (MapMessage) message;

            try {
                String event = mapMessage.getString("event");

                if ("ErrorOccurred".equals(event)) {
                    errorHandler(mapMessage);
                } else if ("CompileFinished".equals(event)) {
                    compileFinishedHandler(mapMessage);
                } else if ("TestPointFinished".equals(event)) {
                    testPointFinishedHandler(mapMessage);
                } else if ("AllTestPointsFinished".equals(event)) {
                    allTestPointsFinishedHandler(mapMessage);
                } else if ("TestFinished".equals(event)) {
                    testFinishedHandler(mapMessage);
                } else if ("KeepAlive".equals(event)) {
                    receiveFromAliveJudgersHandler(mapMessage);
                } else {
                    log.warn(String.format("Unknown Event Received. [Event = %s]", event));
                }
            } catch (JMSException ex) {
                log.error(ex.getMessage());
            }
        }
    }

    /**
     * @Param [mapMessage] [消息队列中收到的MapMessage对象]
     * @Return void []
     * @Author zby
     * @Date 2022/02/05
     * @Description 处理评测机发生内部错误的消息.
     */
    private void errorHandler(MapMessage mapMessage) throws JMSException {
        long submissionId = mapMessage.getLong("submissionId");
        Submission submission = submissionService.getById(submissionId);
        if (submission != null) {
            eventPublisher.publishEvent(
                    new SubmissionEvent(this, submission.getType(), submissionId, submission.getUserId(),
                            submission.getPageId(), "System Error", "", "System Error.", true));
            log.info(String.format("Submission #%d returned [System Error].", submissionId));
        }
    }

    /**
     * @Param [mapMessage] [消息队列中收到的MapMessage对象]
     * @Return void []
     * @Author zby
     * @Date 2022/02/05
     * @Description 处理评测机编译完成时的消息.
     */
    private void compileFinishedHandler(MapMessage mapMessage) throws JMSException {
        long submissionId = mapMessage.getLong("submissionId");
        boolean isSuccessful = mapMessage.getBoolean("isSuccessful");
        String logs = mapMessage.getString("log");

        Submission submission = submissionService.getById(submissionId);
        if (submission != null) {
            if (isSuccessful) {
                String message = "Compile Successfully.\n\n";
                eventPublisher.publishEvent(
                        new SubmissionEvent(this, submission.getType(), submissionId, submission.getUserId(),
                                submission.getPageId(), "Running", "", message, false));
                log.info(String.format("Submission #%d returned [Compile Successfully].", submissionId));
            } else {
                eventPublisher.publishEvent(
                        new SubmissionEvent(this, submission.getType(), submissionId, submission.getUserId(),
                                submission.getPageId(), "Compiler Error", "", logs, true));
                log.info(String.format("Submission #%d returned [Compile Error].\n\tError Message:%s", submissionId, logs));
            }
        }
    }

    /**
     * @Param [mapMessage] [消息队列中收到的MapMessage对象]
     * @Return void []
     * @Author zby
     * @Date 2022/02/05
     * @Description 处理评测机完成单个测试点的消息.
     */
    private void testPointFinishedHandler(MapMessage mapMessage) throws JMSException {
        long submissionId = mapMessage.getLong("submissionId");
        Long checkpointId = mapMessage.getLong("checkpointId");
        String runtimeResult = mapMessage.getString("runtimeResult");
        int usedTime = mapMessage.getInt("usedTime");
        int usedMemory = mapMessage.getInt("usedMemory");
        int score = mapMessage.getInt("score");

        String message = String.format("- Test Point #%d: %s, Time = %d ms, Memory = %d KB, Score = %d\n",
                checkpointService.getNumById(checkpointId), runtimeResult, usedTime, usedMemory, score);
        Submission submission = submissionService.getById(submissionId);
        if (submission != null) {
            eventPublisher.publishEvent(
                    new SubmissionEvent(this, submission.getType(), submissionId, submission.getUserId(),
                            submission.getPageId(), "Running", "", message, false));

            log.info(String.format("Submission #%d / CheckPoint #%d returned [%s] (Time = %dms, Memory = %d KB, Score =" + " %d).",
                    submissionId, checkpointId, runtimeResult, usedTime, usedMemory, score));
        }
    }

    /**
     * @Param [mapMessage] [消息队列中收到的MapMessage对象]
     * @Return void []
     * @Author zby
     * @Date 2022/02/05
     * @Description 处理评测机完成全部测试点的消息.
     */
    private void allTestPointsFinishedHandler(MapMessage mapMessage) throws JMSException {
        long submissionId = mapMessage.getLong("submissionId");
        String runtimeResult = mapMessage.getString("runtimeResult");
        int usedTime = mapMessage.getInt("totalTime");
        int usedMemory = mapMessage.getInt("maxMemory");
        int score = mapMessage.getInt("totalScore");

        String message = String.format("\n%s, Time = %d ms, Memory = %d KB, Score = %d\n", runtimeResult, usedTime, usedMemory, score);

        Submission submission = submissionService.getById(submissionId);
        if (submission != null) {
            eventPublisher.publishEvent(
                    new SubmissionEvent(this, submission.getType(), submissionId, submission.getUserId(),
                            submission.getPageId(), runtimeResult, "", message, true));

            log.info(String.format("Submission #%d judge completed and returned [%s] (Time = %d ms, Memory = %d KB, Score" + " = %d).",
                    submissionId, runtimeResult, usedTime, usedMemory, score));
        }
    }

    /**
     * @Param [mapMessage] [消息队列中收到的MapMessage对象]
     * @Return void []
     * @Author zby
     * @Date 2022/02/05
     * @Description 处理评测机完成测试的消息.
     */
    private void testFinishedHandler(MapMessage mapMessage) throws JMSException {
        long submissionId = mapMessage.getLong("submissionId");
        String runtimeResult = mapMessage.getString("runtimeResult");
        String runtimeOutput = mapMessage.getString("runtimeOutput");
        int usedTime = mapMessage.getInt("usedTime");
        int usedMemory = mapMessage.getInt("usedMemory");

        String message = String.format("\nTime = %d ms, Memory = %d KB\n", usedTime, usedMemory);

        Submission submission = submissionService.getById(submissionId);
        if (submission != null) {
            eventPublisher.publishEvent(
                    new SubmissionEvent(this, submission.getType(), submissionId, submission.getUserId(),
                            submission.getPageId(), runtimeResult, runtimeOutput, message, true));

            log.info(String.format("Submission #%d judge completed and returned [%s] (Time = %d ms, Memory = %d KB).",
                    submissionId, runtimeResult, usedTime, usedMemory));
        }
    }

    /**
     * @Param [mapMessage] [消息队列中收到的MapMessage对象]
     * @Return void []
     * @Author zby
     * @Date 2022/02/05
     * @Description 处理来自评测机的Keep-Alive消息. 用于在Web端获取后端评测机的信息.
     */
    private void receiveFromAliveJudgersHandler(MapMessage mapMessage) throws JMSException {
        String judgerUsername = mapMessage.getString("username");
        String judgerDescription = mapMessage.getString("description");
        long heartbeatTimeInMillis = mapMessage.getLong("heartbeatTime");

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(heartbeatTimeInMillis);
        Date heartbeatTime = calendar.getTime();

        eventPublisher.publishEvent(new KeepAliveEvent(this, judgerUsername, judgerDescription, heartbeatTime));
        log.info(String.format("Received heartbeat from Judger[%s]", judgerUsername));
    }

}
